<?php
/**
 * Ly - 系统数据库操作类
 *
 * 用于调用系统所有数据库驱动类
 * 支持连贯操作
 *
 * Copyright (c) 2013 YinHailin All rights reserved.
 * @Author: YinHailin
 * @Authorize: Boren Network
 * @Created by YinHailin at 2013-11-01
 */

defined('LY_VERSION') or exit();

class MergeCore {

  /* 模块名称 */
  protected $Name = '';
  /* 数据库名称 */
  protected $DbName = '';
  /* 数据表名 */
  protected $TableName = '';
  /* 数据表前缀 */
  protected $TablePrefix = '';
  /* 当前数据库连接 */
  protected $CurDb = null;
  /* 当前数据表字段 */
  protected $Fields = array();
  /* SQL表达式 */
  protected $Options = array();
  /* 当前SQL语句 */
  protected $CurSql = '';

  /*
   * 构造方法
   * @param string $name 数据表名，支持“数据库名.数据表名”方式读取数据
   * @param string $prefix 数据表前缀
   * @param string $connect 数据库连接配置信息
   */
  public function __construct($name, $prefix = '', $connect = '') {
    //获取模块名称
    if (strpos($name, '.')) {
      //若数据表为“数据库名.模块名称”模式
      list($this->DbName, $this->Name) = explode('.', $name);
    } else {
      $this->Name = $name;
    }
    //获取数据库表前缀
    if (is_null($prefix)) {
      //值为null，则无前缀
      $this->TablePrefix = '';
    } elseif ($prefix != '') {
      $this->TablePrefix = $prefix;
    } else {
      $this->TablePrefix = C('DB_PREFIX');
    }
    //获取真实数据表名
    $this->TableName = $this->GetRealTable();
    $this->Options['table'] = $this->TableName;
    //获取数据库操作对象
    $this->Db(0, $connect);
  }

  /*
   * 数据库连接或切换
   * @param string $dbId 数据库连接ID
   * @param array | string $config 数据库连接配置信息，若传递null则表示断开该数据库连接
   * @return object
   */
  public function Db($dbId = '', $config = '') {
    if ($dbId === '' && $this->CurDb) {
      return $this->CurDb;
    }
    static $dbConfList = array();  //数据库配置列表
    static $dbList = array();  //数据库连接列表
    if (!isset($dbIdList[$dbId]) || (isset($dbConfList) && $config && $dbConfList[$dbId] != $config)) {
      //创建新的数据库实例
      if (is_string($config) && !empty($config)) {
        //可读取配置中的值
        $config = C($config);
      }
      $dbList[$dbId] = DbCore::GetInstance($config);
    } elseif (is_null($config)) {
      //关闭数据库连接
      $dbList[$dbId]->Close();
      unset($dbList[$dbId]);
      unset($dbConfList[$dbId]);
      return '';
    }
    //记录连接配置信息
    $dbConfList[$dbId] = $config;
    //切换数据库连接
    $this->CurDb = $dbList[$dbId];
    //获取字段信息
    if (!empty($this->DbName)) {
      $this->FlushField();
    }
    return $this;
  }

  /*
   * 获取并缓存字段信息
   */
  protected function FlushField() {
    $identifier = $this->DbName.'.'.$this->TableName;
    $fileName = RUNTIME_PATH.'Data/'.$identifier.'.php';
    if (empty($this->Fields)) {
      if (C('DB_FIELDS_CACHE') && is_file($fileName)) {
        //缓存存在，直接读取缓存返回
        $this->Fields = include $fileName;
      } else {
        //清空字段缓存
        if (!C('DB_FIELDS_CACHE') && is_file($fileName)) { F($identifier, null);}
        //读取并解析字段信息
        $fields = $this->CurDb->GetFields($this->TableName);
        //无法读取字段信息
        if (!$fields) { return false;}
        $this->Fields = array_keys($fields);
        foreach ($fields as $key => $field) {
          $type[$key] = $field['type'];
          if ($field['primary']) {
            $this->Fields['primary_key'] = $key;
            if ($field['autoinc']) { $this->Field['autoinc'] = true;}
          }
        }
        $this->Fields['type'] = $type;
        //字段缓存
        if (C('DB_FIELDS_CACHE')) {
          F($identifier, $this->Fields);
        }
      }
    }
  }

  /*
   * 获取数据表真实表名
   * @return string
   */
  protected function GetRealTable() {
    if (empty($this->TableName)) {
      $tableName = $this->TablePrefix ? $this->TablePrefix : '';
      $tableName .= parse_name($this->Name, 0);
      $this->TableName = strtolower($tableName);
    }
    return $this->TableName;
  }

  /*
   * 对保存的数据进行字段过滤
   * @param array $data 数据表字段数据
   * @return array
   */
  protected function FilterField($data) {
    //过滤非数据表字段
    if (!empty($this->Fields)) {
      foreach ($data as $key => $value) {
        if (!in_array($key, $this->Fields, true)) {
          unset($data[$key]);
        } else {
          //解析字段类型
          $data = $this->ParseType($data, $key);
        }
      }
    }
    return $data;
  }

  /*
   * 解析字段类型
   * @param array $data 字段数据
   * @param string $key 字段名
   * @return array
   */
  protected function ParseType($data, $key) {
    $fieldType = strtolower($this->fields['type'][$key]);
    if (false === strpos($fieldType, 'bigint') && false !== strpos($fieldType, 'int')) {
      $data[$key] = intval($data[$key]);
    } elseif (false !== strpos($fieldType, 'float') || false !== strpos($fieldType, 'double')) {
      $data[$key] = floatval($data[$key]);
    } elseif (false !== strpos($fieldType, 'bool')) {
      $data[$key] = (bool)$data[$key];
    }
    return $data;
  }

  /*
   * 解析表达式
   * @param array $options 表达式
   */
  protected function ParseOptions() {
    $options = $this->Options;
    //清空SQL表达式，避免影响下次查询
    $this->Options = array();
    $this->Options['table'] = $this->TableName;
    //数据表别名处理
    if (!empty($options['alias'])) {
      $options['table'] .= ' '.$options['alias'];
    }
    return $options;
  }

  /*
   * Alias方法
   * @param string $data SQL数据
   * @return object
   */
  public function Alias($data) {
    if (!empty($data)) {
      $this->Options['alias'] = $data;
    }
    return $this;
  }

  /*
   * Field方法
   * @param string $data SQL数据
   * @return object
   */
  public function Field($data) {
    if (!empty($data)) {
      $this->Options['field'] = $data;
    } else {
      $this->Options['field'] = '*';
    }
    return $this;
  }

  /*
   * Where方法
   * @param string $data SQL数据
   * @return object
   */
  public function Where($data) {
    if (!empty($data)) {
      $this->Options['where'] = $data;
    }
    return $this;
  }

  /*
   * Limit方法
   * @param string $data SQL数据
   * @return object
   */
  public function Limit($data) {
    if (!empty($data)) {
      $this->Options['limit'] = $data;
    }
    return $this;
  }

  /*
   * Order方法
   * @param string $data SQL数据
   * @return object
   */
  public function Order($data) {
    if (!empty($data)) {
      $this->Options['order'] = $data;
    }
    return $this;
  }

  /*
   * Join方法
   * @param array | string $data SQL数据
   * @return object
   */
  public function Join($data) {
    if (!empty($data)) {
      if (is_array($data)) {
        if (is_array($this->Options['join'])) {
          $this->Options['join'] = array_merge($this->Options['join'], $data);
        } else {
          $this->Options['join'] = $data;
        }
      } else {
        $this->Options['join'][] = $data;
      }
    }
    return $this;
  }

  /*
   * Count方法
   * @param array $data 数据
   * @param boolean $replace 是否采用REPLACE语法
   * @return false | integer
   */
  public function Add($data, $replace = false) {
    //分析SQL表达式
    $options = $this->ParseOptions();
    //字段数据处理
    $data = $this->FilterField($data);
    //写入数据库
    $result = $this->CurDb->Insert($data, $options, $replace);
    $this->CurSql = $this->CurDb->GetLastSql();
    if ($result !== false) {
      return $this->CurDb->GetLastId();
    } else {
      return false;
    }
  }

  /*
   * Save方法
   * @param array $data 数据
   * @return false | integer
   */
  public function Save($data) {
    //分析SQL表达式
    $options = $this->ParseOptions();
    //字段数据处理
    $data = $this->FilterField($data);
    //写入数据库
    $result = $this->CurDb->Update($data, $options);
    $this->CurSql = $this->CurDb->GetLastSql();
    return $result;
  }

  /*
   * Delete方法
   * @return false | integer
   */
  public function Delete() {
    //分析SQL表达式
    $options = $this->ParseOptions();
    //写入数据库
    $result = $this->CurDb->Delete($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    return $result;
  }

  /*
   * Select方法
   * @return false | integer | null
   */
  public function Select() {
    //分析SQL表达式
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if ($result === false) { return false;}
    if (empty($result)) { return null;}
    return $result;
  }

  /*
   * Find方法
   * @return false | integer
   */
  public function Find() {
    $this->Options['limit'] = '1';
    //分析SQL表达式
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if ($result === false) { return false;}
    if (empty($result)) { return null;}
    return array_shift($result);
  }

  /*
   * SetField方法
   * @param array | string $field 字段名称或array('字段'=>'字段值')形式数组
   * @param string $value 字段值
   * @return false | integer
   */
  public function SetField($field, $value='') {
    if (is_array($field)) {
      $data = $field;
    } else {
      $data[$field] = $value;
    }
    $result = $this->Save($data);
    $this->CurSql = $this->CurDb->GetLastSql();
    return $result;
  }

  /*
   * GetField方法
   * @param string $field 字段名称
   * @return string | null
   */
  public function GetField($field) {
    $field = trim($field);
    $this->Options['field'] = $field;
    $this->Options['limit'] = 1;
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if (!empty($result) && is_array($result)) {
      return reset($result[0]);
    } else {
      return null;
    }
  }

  /*
   * Count方法
   * @param string $field 字段名称
   * @return integer
   */
  public function Count($field = '') {
    $field = trim($field);
    if ($field == '') {
      $options = $this->ParseOptions();
      $result = $this->CurDb->Select($options);
      $this->CurSql = $this->CurDb->GetLastSql();
      return count($result);
    } else {
      $this->Options['field'] = 'count(`'.$field.'`) AS hy_count';
      $options = $this->ParseOptions();
      //SQL查询
      $result = $this->CurDb->Select($options);
      $this->CurSql = $this->CurDb->GetLastSql();
      if (!empty($result) && is_array($result)) {
        return reset($result[0]);
      } else {
        return 0;
      }
    }
  }

  /*
   * Max方法
   * @param string $field 字段名称
   * @return integer
   */
  public function Max($field) {
    $field = trim($field);
    $this->Options['field'] = 'max(`'.$field.'`) AS hy_max';
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if (!empty($result) && is_array($result)) {
      return reset($result[0]);
    } else {
      return 0;
    }
  }

  /*
   * Min方法
   * @param string $field 字段名称
   * @return integer
   */
  public function Min($field) {
    $field = trim($field);
    $this->Options['field'] = 'min(`'.$field.'`) AS hy_min';
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if (!empty($result) && is_array($result)) {
      return reset($result[0]);
    } else {
      return 0;
    }
  }

  /*
   * Avg方法
   * @param string $field 字段名称
   * @return integer
   */
  public function Avg($field) {
    $field = trim($field);
    $this->Options['field'] = 'avg(`'.$field.'`) AS hy_avg';
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if (!empty($result) && is_array($result)) {
      return reset($result[0]);
    } else {
      return 0;
    }
  }

  /*
   * Sum方法
   * @param string $field 字段名称
   * @return integer
   */
  public function Sum($field) {
    $field = trim($field);
    $this->Options['field'] = 'sum(`'.$field.'`) AS hy_sum';
    $options = $this->ParseOptions();
    //SQL查询
    $result = $this->CurDb->Select($options);
    $this->CurSql = $this->CurDb->GetLastSql();
    if (!empty($result) && is_array($result)) {
      return reset($result[0]);
    } else {
      return 0;
    }
  }

  /*
   * Query方法
   * @param string $sql SQL语句
   * @return array | false
   */
  public function Query($sql) {
    $this->CurSql = $sql;
    return $this->CurDb->Query($sql);
  }

  /*
   * Execute方法
   * @param string $sql SQL语句
   * @return integer | false
   */
  public function Execute($sql) {
    $this->CurSql = $sql;
    return $this->CurDb->Execute($sql);
  }

  public function GetLastSql() {
    return $this->CurSql;
  }
}